package org.mzx.shortLink.adminn.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.mzx.shortLink.adminn.common.ThreadLocal.threadlocal;
import org.mzx.shortLink.adminn.common.ThreadLocal.threadlocalDTO;
import org.mzx.shortLink.starter.convention.Exception.ClientException;
import org.mzx.shortLink.adminn.domain.entity.TGroup;
import org.mzx.shortLink.adminn.dto.Request.ShortLinkGroupSaveReqDTO;
import org.mzx.shortLink.adminn.dto.Request.groupSortDtoReq;
import org.mzx.shortLink.adminn.dto.Request.groupUpdateDtoResp;
import org.mzx.shortLink.adminn.dto.Response.groupTypeDtoResp;
import org.mzx.shortLink.adminn.remoteApi.Api.LinkApi;
import org.mzx.shortLink.adminn.remoteApi.dto.Request.TLinkCountDtoReq;
import org.mzx.shortLink.adminn.service.TGroupService;
import org.mzx.shortLink.adminn.domain.mapper.TGroupMapper;
import org.mzx.shortLink.starter.convention.Result.Result;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.mzx.shortLink.adminn.common.enums.redisEnum.LOCK_GROUP_CREATE;

/**
* @author mazhenxi
* @description 针对表【t_group】的数据库操作Service实现
* @createDate 2024-11-26 17:45:27
*/
@Service
@RequiredArgsConstructor
public class TGroupServiceImpl extends ServiceImpl<TGroupMapper, TGroup>
    implements TGroupService{

    private final TGroupMapper mapper;

    private final LinkApi linkApi;

    private final RedissonClient redissonClient;

    private final RBloomFilter<String> groupCreateCachePenetrationBloomFilter;

    private final RBloomFilter<String> groupNameCreateCachePenetrationBloomFilter;

    @Value("${short-link.group.maxCreateSize}")
    private Integer maxGroupGreatSize;
    @Override
    public Void createGroup(ShortLinkGroupSaveReqDTO requestParam) {
        createGroup(requestParam.getName(),threadlocal.getLoginUser().getUsername());
        return null;
    }


    @SneakyThrows
    @Override
    public Void createGroup(String name, String userName) {
        //先获取分布式锁，如果有多个线程同时访问那么会发生线程安全问题
        RLock lock = redissonClient.getLock(LOCK_GROUP_CREATE+userName);
        boolean tryLock = lock.tryLock(10, TimeUnit.SECONDS);
        boolean nameExist = false;
        if(tryLock){
            try {
                //先查询此用户有几个分组
                Integer groupNum = mapper.findGroupNumByuserName(userName);
                if(groupNum>=maxGroupGreatSize) {
                    throw new ClientException(String.format("您创建的分组数超过最大限制 %d",maxGroupGreatSize));
                }

                nameExist = groupNameCreateCachePenetrationBloomFilter.contains(userName + name);
                if (nameExist) {
                    throw new ClientException("该名字已经存在与你的组中");
                }
                int retryCount = 0;
                int maxCount = 10;
                String randomId = null;
                //首先我们需要生成六位随机数作为我们组的id
                while (retryCount<maxCount) {
                    randomId = RandomUtil.randomString(6);
                    boolean exist = groupCreateCachePenetrationBloomFilter.contains(randomId);
                    if (!exist) {
                        break;
                    }
                }
                TGroup group = TGroup.builder()
                        .gid(randomId)
                        .name(name)
                        .username(userName)
                        .build();
                save(group);
                groupCreateCachePenetrationBloomFilter.add(randomId);
                groupNameCreateCachePenetrationBloomFilter.add(userName + name);
            }catch (Exception e) {
                log.debug(e.getMessage());
            }finally {
                lock.unlock();
            }
        }
        if (nameExist) {
            throw new ClientException("该名字已经存在与你的组中");
        }
        return null;
    }

    @Override
    public List<groupTypeDtoResp> selectGroup() {
        //首先获取当前登陆的用户
        threadlocalDTO loginUser = threadlocal.getLoginUser();
        List<TGroup> list = lambdaQuery().eq(TGroup::getUsername, loginUser.getUsername())
                .eq(TGroup::getDelFlag, 0)
                .orderByAsc(TGroup::getCreateTime,TGroup::getSortOrder)
                .list();
        List<groupTypeDtoResp> respList = BeanUtil.copyToList(list, groupTypeDtoResp.class);
        List<TLinkCountDtoReq> tLinkCountDtoReqs = BeanUtil.copyToList(list, TLinkCountDtoReq.class);
        Result<Map<String, Map<String, Object>>> count = linkApi.count(tLinkCountDtoReqs);
        respList.forEach(each->{
            String gid = each.getGid();
            //这里我们第一次get(gid)获得的是一个Map<String,Object>
            //第二次get(gid)获得的才是count
            Map<String, Object> countMap = count.getData().get(gid);
            //这里有两种情况一种是一条短链接也没有，但是有很多分组，这里传过来会爆空值异常，这里我们在work层处理了
            //另一种情况是有短链接，但是有的分组下没有短链接！
            if (countMap==null) {
                each.setCount(0);
                return;
            }
            each.setCount((Integer)countMap.get("count"));
        });
        return respList;
    }

    @Override
    public Void Update(groupUpdateDtoResp requestParam) {
        String gid = requestParam.getGid();
        String name = requestParam.getName();
        if(StrUtil.isBlank(gid)||StrUtil.isBlank(name)) {
            throw new ClientException("更新失败，传入的名字为空");
        }
        boolean update = lambdaUpdate().eq(TGroup::getGid, gid)
                .set(TGroup::getName, name)
                .update();
        if (!update) {
            throw new ClientException("更新失败");
        }
        return null;
    }

    @Override
    public Void delete(String gid) {
        if (StrUtil.isBlank(gid)) {
            throw new ClientException("传入的gid有误");
        }
        LambdaQueryWrapper<TGroup> Wrapper = new LambdaQueryWrapper<>();
        Wrapper.eq(TGroup::getGid,gid);
        remove(Wrapper);
        return null;
    }

    @Override
    public Void sort(List<groupSortDtoReq> gids) {
        //把多次更新数据库更改为一次更新多条
        mapper.updateBatch(gids);
        return null;
    }

    @Override
    public List<String> selectGidsByUserName(String username) {
        return mapper.selectGidsByUserName(username);
    }
}




